#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os, sys, json

if os.name == 'nt':
    host_spec = ' HOST=win32'
else:
    host_spec = ' HOST=linux'

argv = sys.argv
argc = len(argv)
error_occurred = False

build_log_file = 'build.log'
platform_json = 'system/platform/platform.json'
mklfs_tool = 'tools/mklfs/build/bin/mklfs'
mklfs_tool_src_dir = 'tools/mklfs'

# error process
def error_proc(msg, prompt='CommandError', errtype='EARGV'):
    global error_occurred
    if not error_occurred:
        print("! %s: [%s]: `%s'" % (prompt, errtype, msg))
        error_occurred = True

# help cmd process
def help_proc():
    print('Usage: build.sh <action> [<app>] [[<at>] [<fw_name>]]\n')
    print('These are common commands used in various situations:')
    print('   - mcfg                     Configure features through menuconfig')
    print('   - make <app> [<args>]      Do the compilation work')
    print('   - flash <app>              Flash program to device')
    print('   - clean [<app>]            Clean the build directory')
    print('   - mklfs -c <pack-dir> -b <block-size> -r <read-size> -p <prog-size> -s <filesystem-size> -i <image-file-path>')
    print('                              Make littlefs2 image')
    print('   - help                     Show this help page')

# menuconfig cmd process
def menuconfig_proc():
    cmd_str = 'make mcfg PY=' + argv[1] + host_spec
    os.system(cmd_str)

# clean cmd process
def clean_proc():
    if argc < 3 or argc > 4:
        error_proc('arguments count error')
        return

    if argc == 3:
        clean_target = ''
    else:
        clean_target = argv[3]

    cmd_str = 'make clean CLEAN_TARGET=' + clean_target + ' PY=' + argv[1] + host_spec
    os.system(cmd_str)

# get plat by board
def get_plat_by_board(board):
    if not os.path.exists(platform_json):
        error_proc(platform_json, 'NotFoundError', 'ECONF')
        return ''
    with open(platform_json, 'r', encoding='utf-8') as f:
        platforms = json.load(f)
        for k,v in platforms.items():
            if board in v:
                return k
        return ''

# make cmd process
def make_proc():
    global argv
    global argc
    got_plat = False
    got_noos = False
    got_verbose = False
    got_seq_params = False
    got_fw_name = False
    app_entry = argv[3].replace('\\', '/')

    app_entry_chr_list = list(app_entry)

    for i in list(reversed(range(0, len(app_entry_chr_list)))):
        if app_entry_chr_list[i-len(app_entry_chr_list)] == '/':
            app_entry_chr_list[i-len(app_entry_chr_list)] = ''
        else:
            break

    app_entry = ''.join(app_entry_chr_list)

    if not len(app_entry) or not os.path.exists(os.path.abspath(app_entry)):
        error_proc(argv[3], 'NotFoundError', 'EARGV')
        return
    
    cmd_str = argv[2] + ' APP_ENTRY=' + app_entry
    
    if argc != 4:
        for arg in argv[4:]:
            if arg.find('@') >= 0: # sequential parameters process
                if got_seq_params:
                    error_proc(arg)
                    return
                else:
                    seq_params = arg.split('@')
                    for i in seq_params:
                        if i == '':
                            pass
                        elif get_plat_by_board(i):
                            if got_plat:
                                error_proc(i)
                                return
                            else:
                                cmd_str = cmd_str + ' PLAT=' + get_plat_by_board(i) + ' BOARD=' + i
                                got_plat = True
                        elif i == 'NOOS':
                            if got_noos:
                                error_proc(i)
                                return
                            else:
                                cmd_str = cmd_str + ' NOOS=y'
                                got_noos = True
                        elif i == 'verbose':
                            if got_verbose:
                                error_proc(i)
                                return
                            else:
                                cmd_str = cmd_str + ' V=1'
                                got_verbose = True
                        else:
                            error_proc(i)
                            return
                    got_seq_params = True
            else:
                if got_fw_name:
                    error_proc(arg)
                    return
                else:
                    cmd_str = cmd_str + ' FW_NAME=' + arg
                    got_fw_name = True
    
    cmd_str = cmd_str + host_spec
    cmd_str = cmd_str + ' PY=' + argv[1]
    build_log_dir = f'build/{app_entry}/log'
    cmd_str = cmd_str + f' 2>&1 | tee {build_log_dir}/{build_log_file}'

    if not os.path.exists(build_log_dir):
        os.makedirs(build_log_dir)
    os.system(cmd_str)

# flash cmd process
def flash_proc():
    global argv
    global argc
    
    if argc != 4:
        error_proc('arguments count error')
        return
    else:
        app_entry = argv[3].replace('\\', '/')

        app_entry_chr_list = list(app_entry)

        for i in list(reversed(range(0, len(app_entry_chr_list)))):
            if app_entry_chr_list[i-len(app_entry_chr_list)] == '/':
                app_entry_chr_list[i-len(app_entry_chr_list)] = ''
            else:
                break

        app_entry = ''.join(app_entry_chr_list)

        if not len(app_entry) or not os.path.exists(os.path.abspath(app_entry)):
            error_proc(argv[3], 'NotFoundError', 'EARGV')
            return
        
        cmd_str = 'make flash APP_ENTRY=' + app_entry + ' PY=' + argv[1] + host_spec
        os.system(cmd_str)

def mklfs_proc():
    global argv
    global argc

    argv[2] = mklfs_tool

    if not os.path.exists(mklfs_tool):
        os.system('make -C ' + mklfs_tool_src_dir)
        print()

    cmd_str = ' '.join(argv[2:])
    os.system(cmd_str)

# main entry:
if __name__ == '__main__':
    if argc == 2:
        help_proc()
    elif argc == 3:
        if argv[2] == 'help':
            help_proc()
        elif argv[2] == 'mcfg':
            menuconfig_proc()
        elif argv[2] == 'clean':
            clean_proc()
        elif argv[2] == 'mklfs':
            mklfs_proc()
        else:
            error_proc(argv[2])
    else:
        if argv[2] == 'make':
            make_proc()
        elif argv[2] == 'flash':
            flash_proc()
        elif argv[2] == 'clean':
            clean_proc()
        elif argv[2] == 'mklfs':
            mklfs_proc()
        else:
            error_proc(argv[2])
